package com.sharemedia.managers
{
	import com.sharemedia.consts.AppConst;
	import com.sharemedia.events.FeedbackEvent;
	import com.sharemedia.events.PeerEvent;
	import com.sharemedia.events.TrackEvent;
	import com.sharemedia.interfaces.IAudioManager;
	import com.sharemedia.managers.db.TrackDataBase;
	import com.sharemedia.managers.db.UpdateRatingCommand;
	import com.sharemedia.net.GroupPeer;
	import com.sharemedia.net.StreamPeer;
	import com.sharemedia.utils.ConvertUtil;
	import com.sharemedia.utils.LogUtil;
	import com.sharemedia.vos.Track;
	import com.sharemedia.vos.TrackInfoPacket;
	import com.sharemedia.vos.TrackPacket;
	import com.sharemedia.vos.TrackPrompt;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.SampleDataEvent;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;
	
	import mx.collections.ArrayList;
	import mx.logging.ILogger;
	import mx.utils.UIDUtil;
	
	[Event(name="play", type="com.sharemedia.events.TrackEvent")]
	[Event(name="pause", type="com.sharemedia.events.TrackEvent")]
	[Event(name="musicStreamConnected", type="flash.events.Event")]
	public class AudioServer extends EventDispatcher implements IAudioManager
	{
		private var logger:ILogger;
		private var musicStreamPeer:GroupPeer;
		private var feedbackPeer:StreamPeer;
		private var offerPeer:StreamPeer;
		
		public function AudioServer()
		{
			logger = LogUtil.getLogger(this);
			musicStreamPeer = new GroupPeer();
			feedbackPeer = new StreamPeer();
			offerPeer = new StreamPeer();
			musicStreamPeer.addEventListener(PeerEvent.PEER_CONNECTED, musicStreamConnectedHandler);
			musicStreamPeer.addEventListener("memberCountChanged", memberCountChangedHandler);
			
			feedbackPeer.addEventListener(FeedbackEvent.FEEDBACK_RECEIVED, feedbackHandler);
			offerPeer.addEventListener(FeedbackEvent.FEEDBACK_RECEIVED, offerHandler);
			
			sound = new Sound();
			sound.addEventListener(SampleDataEvent.SAMPLE_DATA, sampleDataReceiver);
		}
		
		private var chanel:SoundChannel;
		
		private var sound:Sound;
		private var soundSource:Sound;
		
		private var _volume:Number = 1;
		
		[Bindable]
		public function get volume():Number
		{
			return _volume;
		}
		
		public function set volume(value:Number):void
		{
			_volume = value;
			
			if(chanel)
				chanel.soundTransform = new SoundTransform(_volume);
		}
		
		private var _trackList:TrackList = new TrackList();
		
		public function get trackList():TrackList
		{
			return _trackList;
		}
		
		public function isPlaying():Boolean
		{
			return chanel != null;
		}
		
		public function play(track:Track=null):void
		{
			if(chanel)
				chanel.stop();
			
			if(track)
				trackList.currentTrack = track;
			else
				track = trackList.currentTrack;
			
			track.broadcastUid = UIDUtil.createUID();
			soundSource = new Sound();
			soundSource.load(new URLRequest(track.path));
			soundSource.addEventListener(Event.COMPLETE, soundSourceLoadedHandler);
			soundSource.addEventListener(IOErrorEvent.IO_ERROR, soundSourceIOErrorHandler);
			
			dispatchEvent(new TrackEvent(TrackEvent.PLAY, track));
			
			dispatchEvent(new Event("currentTrackChanged"));
			
			generatePrompt();
		}
		
		[Bindable("memberCountChanged")]
		public function get memberCount():Number
		{
			return musicStreamPeer ? musicStreamPeer.memberCount : 0;
		}
		
		[Bindable("positionChanged")]
		public function get position():Number
		{
			return sourceChanel ? sourceChanel.position : 0;
		}
		
		public function pause():void
		{
			if(chanel)
			{
				chanel.stop();
				chanel = null;
			}
			
			dispatchEvent(new TrackEvent(TrackEvent.PAUSE, currentTrack));
		}
		
		[Bindable]
		public var shuffle:Boolean = false;
		
		public function nextTrack():void
		{
			if(prompt && prompt.length > 0)
			{
				var track:TrackPrompt;
				for each (var tp:TrackPrompt in prompt.source) 
				{
					if(track)
					{
						if(track.votes < tp.votes)
						{
							track = tp;
						}
					}
					else
					{
						track = tp;
					}
				}
				if(track)
					trackList.setNextTrack(track);
			}
			play(trackList.nextTrack(shuffle));
		}
		
		
		public function prevTrack():void
		{
			play(trackList.prevTrack());
		}
		
		public function like():void
		{
			currentTrack.likeNum++;
			TrackDataBase.getInstance().execute(new UpdateRatingCommand(currentTrack));
		}
		
		public function unlike():void
		{
			currentTrack.unlikeNum++;
			TrackDataBase.getInstance().execute(new UpdateRatingCommand(currentTrack));
		}
		
		public function offer(uid:String):void
		{
			
		}
		
		[Bindable("currentTrackChanged")]
		public function get currentTrack():Track
		{
			return trackList.currentTrack;
		}
		
		private var _serverName:String;
		public function get serverName():String
		{
			return _serverName;
		}
		
		
		public function init(streamName:String):void
		{
			if(musicStreamPeer)
				musicStreamPeer.init(streamName);
			
			if(feedbackPeer)
				feedbackPeer.init(streamName + "-Feedback", true);
			
			if(offerPeer)
				offerPeer.init(streamName + "-Offer", true);
			
			_serverName = streamName;
		}
		
		protected function soundSourceLoadedHandler(event:Event):void
		{
			chanel = sound.play();
			chanel.soundTransform = new SoundTransform(_volume);
			chanel.addEventListener(Event.SOUND_COMPLETE, soundCompleteHandler);
			
			sourceChanel = soundSource.play(0,0,new SoundTransform(0));
		}
		
		private var sourceChanel:SoundChannel;
		
		private var index:int = 0;
		protected function sampleDataReceiver(event:SampleDataEvent):void
		{
			var bytes:ByteArray = new ByteArray();
			soundSource.extract(bytes, AppConst.SOUND_CHUNK_SIZE)
				
			if(bytes.length > 0)
			{
				event.data.writeBytes(bytes);
				var packet:TrackPacket = new TrackPacket(feedbackPeer.peerId, currentTrack.uid, currentTrack.broadcastUid, position, bytes, new Date().getTime());
				if(Math.ceil(index/AppConst.SEND_TRACK_INFO_DELAY) == index/AppConst.SEND_TRACK_INFO_DELAY)
				{
					packet.info = new TrackInfoPacket(currentTrack.info.name,currentTrack.info.artist,currentTrack.rating, currentTrack.length);
					
					packet.prompt = prompt.source;
				}
				
				musicStreamPeer.post(packet);
			}
			dispatchEvent(new Event("positionChanged"));
			index++;
		}
		
		protected function soundCompleteHandler(event:Event):void
		{
			nextTrack();
		}
		
		private function generatePrompt():void
		{
			prompt.removeAll();
			prompt.addItem(ConvertUtil.toTrackPrompt(trackList.getShuffledTrack()));
			prompt.addItem(ConvertUtil.toTrackPrompt(trackList.getShuffledTrack()));
			prompt.addItem(ConvertUtil.toTrackPrompt(trackList.getShuffledTrack()));
		}
		
		protected function soundSourceIOErrorHandler(event:Event):void
		{
			nextTrack();
		}
		
		protected function musicStreamConnectedHandler(event:Event):void
		{
			dispatchEvent(new Event("musicStreamConnected"));
			
			_conncted = true;
		}
		
		private var _conncted:Boolean;
		
		public function get connected():Boolean
		{
			return _conncted;
		}
		
		protected function memberCountChangedHandler(event:Event):void
		{
			dispatchEvent(event);
		}
		
		protected function feedbackHandler(event:FeedbackEvent):void
		{
			if(event.uid == currentTrack.uid)
			{
				if(event.like)
					like();
				else
					unlike();
			}
		}
		
		protected function offerHandler(event:FeedbackEvent):void
		{
			for each (var track:TrackPrompt in prompt.source) 
			{
				if(track.uid == event.uid)
				{
					track.votes ++;
					return;
				}
			}
		}
		
		[Bindable]
		public var prompt:ArrayList = new ArrayList();
		
		public function get bufferSize():Number
		{
			return 0;
		}
		
		public function set bufferSize(value:Number):void
		{
			
		}
		
	}
}